package com.ruoyi.app.service.impl;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.app.aspect.PermissionAspect;
import com.ruoyi.app.domain.*;
import com.ruoyi.app.dto.*;
import com.ruoyi.app.enume.CollectEnum;
import com.ruoyi.app.enume.OrderStatusEnum;
import com.ruoyi.app.mapper.*;
import com.ruoyi.app.service.UserService;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.RedisKeyConstant;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.AESUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.redis.RedisCache;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

/*
@description:用户服务
@ClassName UserServiceImpl
@author chen
@create 2025-03-13 22:44
@Version 1.0
*/
@Slf4j
@Service
public class UserServiceImpl implements UserService
{

    // 令牌秘钥
    @Value("${token.secret}")
    private String secret;

    // 令牌有效期（默认30分钟）
    @Value("${token.expireTime}")
    private int expireTime;

    @Resource
    @Qualifier
    private UserMapper userMapper;
    @Resource
    @Qualifier
    private BCryptPasswordEncoder passwordEncoder;
    @Resource
    @Qualifier
    private RelUserMemberMapper relUserMemberMapper;

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RedisCache redisCache;

    @Resource
    private WxMaService wxMaService;
    @Resource
    private PromotionInfoMapper promotionInfoMapper;
    @Resource
    private ArticleInfoMapper articleInfoMapper;
    @Resource
    private ArticleThumbsMapper articleThumbsMapper;
    @Resource
    private ArticleCommentMapper articleCommentMapper;
    @Resource
    private TripThumbsMapper tripThumbsMapper;
    @Resource
    private TripCommentMapper tripCommentMapper;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private MemberMapper memberMapper;
    @Resource
    private UserVisitMapper userVisitMapper;
    @Resource
    private VisitMapper visitMapper;
    @Resource
    private OrderInfoMapper orderInfoMapper;
    @Resource
    private CollectMapper collectMapper;

    protected static final long MILLIS_SECOND = 1000;
    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    /*
     * @param user
     * @==============History===============<br/>;
     * @Description //   用户登录
     * @Date 2025/3/14
     * @Author < a href="chencheng@prmt.net">chen</ a>;
     */
    @Override
    public AjaxResult login(User user)
    {
        if(null == user)
        {
            return AjaxResult.error("用户信息不能为空");
        }
        UserExample userExample = new UserExample();
        userExample.createCriteria().andPhoneEqualTo(user.getPhone());
        if(userMapper.countByExample(userExample) <= 0) return AjaxResult.error("当前用户未注册,请先注册");

        List<User> users = userMapper.selectByExample(userExample);
        User dbUser = users.get(0);
        if(dbUser.getDisable())
        {
            return AjaxResult.error("当前用户已被禁用,请联系管理员处理");
        }

        if(!passwordEncoder.matches(user.getPassword(),dbUser.getPassword()))
        {
            return AjaxResult.error("手机号或密码错误");
        }
        return getAjaxResult(dbUser);
    }

    private AjaxResult getAjaxResult(User dbUser)
    {
        LoginUserDto loginUser = new LoginUserDto();
        BeanUtils.copyProperties(dbUser,loginUser);
        MemberDto memberDto = relUserMemberMapper.selectMemberInfoByUserId(loginUser.getId());
        loginUser.setMemberInfo(memberDto);
        loginUser.setIpAddr(IpUtils.getIpAddress(ServletUtils.getRequest()));
        String token = createToken(loginUser);
        return AjaxResult.success().put(Constants.TOKEN,token);
    }


    /*
     * @==============History===============<br/>
     * @Description //   获取用户列表
     * @Date 2025-3-13 22:54
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.page.TableDataInfo
     */
    @Override
    public List<UserDto> getUserInfoList(Map<String, Object> params)
    {
        List<User> userList = userMapper.selectAllList();
        if(null != userList)
        {
            return userList.stream().map(UserDto::applyList).collect(Collectors.toList());
        }
        return null;
    }



    /*
     * @==============History===============<br/>
     * @Description //   微信小程序授权
     * @Date 2025-3-16 0:25
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult wxLogin(UserWxLoginReqDto wxLoginReqDto)
    {
        String sessionKey;
        String openid;
        try {
            WxMaJscode2SessionResult wxMaJscode2SessionResult = wxMaService.getUserService()
                    .getSessionInfo(wxLoginReqDto.getCode());
            openid = wxMaJscode2SessionResult.getOpenid();
            sessionKey = wxMaJscode2SessionResult.getSessionKey();
        } catch (WxErrorException e)
        {
            e.printStackTrace();
            return AjaxResult.error(e.getMessage());
        }

        // 从token中获取用户信息
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();

        if (StrUtil.isNotBlank(wxLoginReqDto.getRawData())
                && StrUtil.isNotBlank(wxLoginReqDto.getSignature())
                && StrUtil.isNotBlank(wxLoginReqDto.getIv())
                && StrUtil.isNotBlank(wxLoginReqDto.getEncryptedData()))
        {


            // 用户信息校验
            Assert.isTrue(
                    wxMaService.getUserService().checkUserInfo(sessionKey, wxLoginReqDto.getRawData(),
                            wxLoginReqDto.getSignature()),
                    () -> new BaseException("用户信息校验失败"));

            // 解密用户信息
            WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(sessionKey,
                    wxLoginReqDto.getEncryptedData(), wxLoginReqDto.getIv());



            loginUserDto.setWxId(openid);
            User updateUser = new User();
            updateUser.setId(loginUserDto.getId());
            updateUser.setUpdateTime(new Date());
            updateUser.setWxId(openid);
            updateUser.setAvatarUrl(userInfo.getAvatarUrl());
            updateUser.setNickName(userInfo.getNickName());
            if(userMapper.updateByPrimaryKeySelective(updateUser) <= 0)
            {
                return AjaxResult.error("用户授权微信失败");
            }

            // 更新这个token
            refreshToken(loginUserDto);
        }
        return AjaxResult.success();
    }

    /*
     * @==============History===============<br/>
     * @Description //   获取用户推广记录
     * @Date 2025-3-18 22:21
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return java.util.List<com.ruoyi.app.domain.PromotionInfo>
     */
    @Override
    public List<PromotionInfo> getPromotionList(Map<String, Object> params)
    {
        return promotionInfoMapper.getPromotionList(params);
    }

    /*
     * @==============History===============<br/>;
     * @Description //   修改用户信息
     * @Date 2025/3/20
     * @Author < a href="chencheng@prmt.net">chen</ a>;
     */
    @Override
    public AjaxResult updateUserInfo(User user)
    {
        user.setUpdateTime(new Date());
        if(StringUtils.isNotEmpty(user.getPassword()))
        {
            user.setPassword(passwordEncoder.encode(user.getPassword()));
        }
        userMapper.updateByPrimaryKeySelective(user);
        User dbUser = userMapper.selectByPrimaryKey(user.getId());
        LoginUserDto loginUser = new LoginUserDto();
        BeanUtils.copyProperties(dbUser,loginUser);
        MemberDto memberDto = relUserMemberMapper.selectMemberInfoByUserId(loginUser.getId());
        loginUser.setMemberInfo(memberDto);
        loginUser.setIpAddr(IpUtils.getIpAddress(ServletUtils.getRequest()));
        // 刷新当前用户信息
        refreshToken(loginUser);
        return AjaxResult.success();
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户文章点赞
     * @Date 2025-3-23 9:20
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult upArticleInfoThumbs(Long id)
    {
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        Long userId = loginUserDto.getId();
        ArticleThumbsExample articleThumbsExample = new ArticleThumbsExample();
        articleThumbsExample.createCriteria().andArticleIdEqualTo(id).andCreateIdEqualTo(userId).andDisableEqualTo(false);
        List<ArticleThumbs> articleThumbsList = articleThumbsMapper.selectByExample(articleThumbsExample);
        if(null != articleThumbsList && articleThumbsList.size() >0)
        {
            return AjaxResult.error("当前用户已对文章进行点赞!");
        }
        String nickName = loginUserDto.getNickName();
        ArticleThumbs articleThumbs = new ArticleThumbs();
        articleThumbs.setArticleId(id);
        articleThumbs.setCreateId(userId);
        articleThumbs.setCreateName(nickName);
        articleThumbs.setCreateTime(new Date());
        articleThumbs.setUpdateId(userId);
        articleThumbs.setUpdateTime(new Date());
        articleThumbs.setDisable(false);
        articleThumbs.setArticleThumbsIp(IpUtils.getIpAddress(ServletUtils.getRequest()));
        transactionTemplate.execute(transactionStatus -> {
            articleThumbsMapper.insertSelective(articleThumbs);
            articleInfoMapper.upArticleInfoThumbs(id);
            return null;
        });
        return AjaxResult.success();
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户取消点赞
     * @Date 2025-3-23 9:31
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult downArticleInfoThumbs(Long id)
    {
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        Long userId = loginUserDto.getId();
        ArticleThumbsExample articleThumbsExample = new ArticleThumbsExample();
        articleThumbsExample.createCriteria().andArticleIdEqualTo(id).andCreateIdEqualTo(userId).andDisableEqualTo(false);
        List<ArticleThumbs> articleThumbsList = articleThumbsMapper.selectByExample(articleThumbsExample);
        if(null != articleThumbsList && articleThumbsList.size() > 0)
        {
            ArticleThumbsExample updateArticleThumbsExample = new ArticleThumbsExample();
            updateArticleThumbsExample.createCriteria().andArticleIdEqualTo(id).andCreateIdEqualTo(userId).andDisableEqualTo(false);
            ArticleThumbs updateArticleThumbs = new ArticleThumbs();
            updateArticleThumbs.setDisable(true);
            transactionTemplate.execute(transactionStatus -> {
                articleThumbsMapper.updateByExampleSelective(updateArticleThumbs,updateArticleThumbsExample);
                articleInfoMapper.downArticleInfoThumbs(id);
                return null;
            });
        }
        else
        {
            return AjaxResult.error("当前用户未对文章进行点赞!");
        }

        return AjaxResult.success();
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户评论文章
     * @Date 2025-3-24 19:58
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult insertArticleCommentInfo(ArticleComment articleComment)
    {
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        articleComment.setCreateId(loginUserDto.getId());
        articleComment.setDisable(false);
        articleComment.setUpdateId(loginUserDto.getId());
        articleComment.setCreateTime(new Date());
        articleComment.setUpdateTime(new Date());
        articleComment.setArticleIp(IpUtils.getIpAddress(ServletUtils.getRequest()));
        return articleCommentMapper.insertSelective(articleComment) > 0 ? AjaxResult.success() : AjaxResult.error();
    }


    /*
     * @==============History===============<br/>
     * @Description //   删除文章评论
     * @Date 2025-3-24 20:06
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult deleteArticleCommentInfo(Long id)
    {
        ArticleCommentExample articleCommentExample = new ArticleCommentExample();
        articleCommentExample.createCriteria().andIdEqualTo(id).andCreateIdEqualTo(PermissionAspect.loginUser.get().getId());
        return articleCommentMapper.deleteByExample(articleCommentExample)  > 0 ? AjaxResult.success() : AjaxResult.error();
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户行程点赞
     * @Date 2025-3-27 21:47
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult upTripInfoThumbs(Long id)
    {
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        Long userId = loginUserDto.getId();
        TripThumbsExample tripThumbsExample = new TripThumbsExample();
        tripThumbsExample.createCriteria().andTripIdEqualTo(id).andCreateIdEqualTo(userId).andDisableEqualTo(false);

        List<TripThumbs> tripThumbList = tripThumbsMapper.selectByExample(tripThumbsExample);
        if(null != tripThumbList && tripThumbList.size() >0)
        {
            return AjaxResult.error("当前用户已对行程进行点赞!");
        }

        String nickName = loginUserDto.getNickName();
        TripThumbs tripThumbs = new TripThumbs();
        tripThumbs.setTripId(id);
        tripThumbs.setCreateId(userId);
        tripThumbs.setCreateName(nickName);
        tripThumbs.setCreateTime(new Date());
        tripThumbs.setUpdateId(userId);
        tripThumbs.setUpdateTime(new Date());
        tripThumbs.setDisable(false);
        tripThumbs.setReady(false);
        tripThumbs.setTripThumbsIp(IpUtils.getIpAddress(ServletUtils.getRequest()));
        transactionTemplate.execute(transactionStatus -> {
            tripThumbsMapper.insertSelective(tripThumbs);
            tripThumbsMapper.upTripInfoThumbs(id);
            return null;
        });
        return AjaxResult.success();
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户取消行程点赞
     * @Date 2025-3-27 21:47
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult downTripInfoInfoThumbs(Long id)
    {
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        Long userId = loginUserDto.getId();
        TripThumbsExample tripThumbsExample = new TripThumbsExample();
        tripThumbsExample.createCriteria().andTripIdEqualTo(id).andCreateIdEqualTo(userId).andDisableEqualTo(false);
        List<TripThumbs> tripThumbList = tripThumbsMapper.selectByExample(tripThumbsExample);
        if(null != tripThumbList && tripThumbList.size() > 0)
        {
            TripThumbsExample updateTripThumbsExample = new TripThumbsExample();
            updateTripThumbsExample.createCriteria().andTripIdEqualTo(id).andCreateIdEqualTo(userId).andDisableEqualTo(false);
            TripThumbs updateTripThumbs = new TripThumbs();
            updateTripThumbs.setDisable(true);
            transactionTemplate.execute(transactionStatus -> {
                tripThumbsMapper.updateByExampleSelective(updateTripThumbs,updateTripThumbsExample);
                tripThumbsMapper.downTripInfoThumbs(id);
                return null;
            });
        }
        else
        {
            return AjaxResult.error("当前用户未对行程进行点赞!");
        }
        return AjaxResult.success();
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户评论行程
     * @Date 2025-3-27 21:48
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult insertTripInfoCommentInfo(TripComment tripComment)
    {
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        tripComment.setCreateId(loginUserDto.getId());
        tripComment.setDisable(false);
        tripComment.setReady(false);
        tripComment.setUpdateId(loginUserDto.getId());
        tripComment.setCreateTime(new Date());
        tripComment.setUpdateTime(new Date());
        tripComment.setTripIp(IpUtils.getIpAddress(ServletUtils.getRequest()));
        return tripCommentMapper.insertSelective(tripComment) > 0 ? AjaxResult.success() : AjaxResult.error();
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户删除评论行程
     * @Date 2025-3-27 21:48
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult deleteTripInfoCommentInfo(Long id)
    {
        TripCommentExample tripCommentExample = new TripCommentExample();
        tripCommentExample.createCriteria().andIdEqualTo(id).andCreateIdEqualTo(PermissionAspect.loginUser.get().getId());
        return tripCommentMapper.deleteByExample(tripCommentExample)  > 0 ? AjaxResult.success() : AjaxResult.error();
    }

    /*
     * @==============History===============<br/>
     * @Description //   删除用户
     * @Date 2025-3-27 21:49
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult deleteByPrimaryKey(Long id)
    {
        return userMapper.deleteByPrimaryKey(id) > 0 ? AjaxResult.success() : AjaxResult.error();
    }


    /*
     * @==============History===============<br/>
     * @Description //   导入用户数据
     * @Date 2025-3-29 20:37
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return org.springframework.http.ResponseEntity<java.lang.String>
     */
    @Override
    public ResponseEntity<String> uploadExcel(MultipartFile file)
    {
        try (InputStream is = file.getInputStream();
             Workbook workbook = new HSSFWorkbook(is)) {  // 使用HSSF处理.xls

            Sheet sheet = workbook.getSheetAt(0);

            // 跳过标题行（索引从0开始）
            for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++)
            {
                Row row = sheet.getRow(rowIndex);
                if (row == null) continue;

                // 按列索引提取数据
                String name = row.getCell(0).getStringCellValue();
                String phone = row.getCell(1).getStringCellValue();
                String grade = row.getCell(2).getStringCellValue();
                String signature = row.getCell(3).getStringCellValue();
                String date = row.getCell(6).getStringCellValue();
                User user = new User();
                user.setName(name);
                user.setPhone(phone);
                user.setSignature(signature);
                user.setPassword(passwordEncoder.encode(phone));
                user.setNickName(name);
                user.setCreateTime(DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, date));
                user.setBalance(new BigDecimal("0"));
                user.setDisable(false);
                user.setUpdateTime(new Date());
                user.setInvitationCode(AESUtils.encrypt(user.getPhone()));

                userMapper.insertSelective(user);

                Member member =  memberMapper.selectByGrade(grade);

               if(null != member)
               {
                   RelUserMember relUserMember = new RelUserMember();
                   relUserMember.setUserId(user.getId());
                   relUserMember.setMemberId(member.getId());
                   relUserMember.setDisable(false);
                   relUserMember.setCreateTime(new Date());
                   relUserMember.setExpirationTime(DateUtils.addMonthWithLocalDate(new Date(),30));
                   relUserMember.setUpdateTime(new Date());
                   relUserMemberMapper.insertSelective(relUserMember);
               }

            }
        } catch (IOException e)
        {
            throw new RuntimeException(e);
        }
        return ResponseEntity.ok("文件处理成功");
    }





    /*
     * @==============History===============<br/>;
     * @Description // 用户注册
     * @Date 2025/3/13
     * @Author < a href="chencheng@prmt.net">chen</ a>;
     */
    @Override
    public AjaxResult insertUserInfo(User user)
    {
        if(null == user)
        {
            return AjaxResult.error("用户信息不能为空");
        }

//        校验手机是否已注册(防止并发操作)
        Lock lock = redissonClient.getLock(RedisKeyConstant.PHONE_CHANGE + user.getPhone());
        try
        {
            lock.lock();
            UserExample userExample = new UserExample();
            userExample.createCriteria().andPhoneEqualTo(user.getPhone());
            if(userMapper.countByExample(userExample) > 0) return AjaxResult.error("该手机号已被注册");
//            插入数据,用户密码加密
            String encode = passwordEncoder.encode(user.getPassword());
            user.setNickName(user.getName());
            user.setPassword(encode);
            user.setCreateTime(new Date());
            user.setUpdateTime(new Date());
            user.setBalance(new BigDecimal("0"));
            user.setDisable(false);
            String invitationCode = user.getInvitationCode();
            user.setInvitationCode(AESUtils.encrypt(user.getPhone()));
            userMapper.insertSelective(user);
            if(StringUtils.isNotEmpty(invitationCode))
            {
                userExample = new UserExample();
                userExample.createCriteria().andInvitationCodeEqualTo(invitationCode);
                List<User> users = userMapper.selectByExample(userExample);
                if(null != users && users.size() > 0)
                {
                    User preatUser = users.get(0);
                    User updateUser = new User();
                    updateUser.setParentId(preatUser.getId());
                    updateUser.setId(user.getId());
                    userMapper.updateByPrimaryKeySelective(updateUser);
                    // 插入推广记录
                    PromotionInfo promotionInfo = new PromotionInfo();
                    promotionInfo.setUserId(updateUser.getId());
                    promotionInfo.setUpdateId(preatUser.getId());
                    promotionInfo.setCreateId(preatUser.getId());
                    promotionInfo.setCreateTime(new Date());
                    promotionInfo.setUpdateTime(new Date());
                    promotionInfoMapper.insertSelective(promotionInfo);
                }
            }
            return AjaxResult.success();
        }catch (Exception exception)
        {
            exception.printStackTrace();
            return AjaxResult.error();
        }finally {
            lock.unlock();
        }
    }


    /*
     * @==============History===============<br/>;
     * @Description //   创建token
     * @Date 2025/3/14
     * @Author < a href="chencheng@prmt.net">chen</ a>;
     */
    public String createToken(LoginUserDto loginUserDto)
    {
        String token = loginUserDto.getInvitationCode();
        // 刷新令牌有效期
        refreshToken(loginUserDto);
        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, token);
        token = Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret).compact();
        return token;
    }


    /*
     * @==============History===============<br/>;
     * @Description //   刷新令牌有效期
     * @Date 2025/3/14
     * @Author < a href="chencheng@prmt.net">chen</ a>;
     */
    @Override
    public void refreshToken(LoginUserDto loginUser)
    {
        loginUser.setLoginTime(System.currentTimeMillis());//登陆时间
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
        String userKey = getTokenKey(loginUser.getInvitationCode());
        redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
    }

    @Override
    public User selectByPrimaryKey(Long createId)
    {
        return userMapper.selectByPrimaryKey(createId);
    }

    @Override
    public void addBalance(Long userId, BigDecimal amount)
    {
        userMapper.addBalance(userId,amount);
    }

    /*
     * @==============History===============<br/>
     * @Description //   小程序访问
     * @Date 2025-4-15 0:10
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult getVisit()
    {
        Visit visit = new Visit();
        visit.setDisable(false);
        visit.setCreateTime(new Date());
        visit.setUpdateTime(new Date());
        return visitMapper.insertSelective(visit) > 0 ? AjaxResult.success() : AjaxResult.error();
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户访问
     * @Date 2025-4-15 0:10
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult getUserVisit()
    {
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        UserVisit userVisit = new UserVisit();
        userVisit.setUserId(loginUserDto.getId());
        userVisit.setDisable(false);
        userVisit.setCreateTime(new Date());
        userVisit.setUpdateTime(new Date());
        return userVisitMapper.insertSelective(userVisit) > 0 ? AjaxResult.success() : AjaxResult.error();
    }


    @Override
    public AjaxResult payOrder(RelUserMember relUserMember)
    {
        Member member = memberMapper.selectByPrimaryKey(relUserMember.getMemberId());
        RelUserMember insertRelUserMember = new RelUserMember();
        insertRelUserMember.setUserId(relUserMember.getUserId());
        insertRelUserMember.setCreateTime(new Date());
        insertRelUserMember.setDisable(false);
        insertRelUserMember.setUpdateTime(new Date());
        insertRelUserMember.setMemberId(member.getId());
        insertRelUserMember.setExpirationTime(DateUtils.addMonthWithLocalDate(new Date(),member.getMemberDate()));

        String orderNo = UUID.randomUUID().toString().replace("-","");
        OrderInfo orderInfo = new OrderInfo();
        log.info("系统充值生成订单号:" + orderNo);
        orderInfo.setOrderNo(orderNo);
        orderInfo.setCreateTime(new Date());
        orderInfo.setUpdateTime(new Date());
        orderInfo.setMemberId(relUserMember.getMemberId());
        orderInfo.setMoney(member.getMemberPrice());
        orderInfo.setNote("系统充值");
        orderInfo.setOrderState(OrderStatusEnum.PAYED.getCode().toString());

        transactionTemplate.execute(transactionStatus -> {
            relUserMemberMapper.insertSelective(insertRelUserMember);
            orderInfoMapper.insertSelective(orderInfo);
            return null;
        });

        return AjaxResult.success();
    }



    /*
     * @==============History===============<br/>
     * @Description //   手机号一键登录
     * @Date 2025-4-8 19:55
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult phoneLogin(User queryUser) throws Exception
    {
        String accessToken = getAccessToken();
        cn.hutool.json.JSONObject phoneNumberObj = getPhoneNumber(queryUser.getCode(), accessToken);
        System.out.println(phoneNumberObj);
        cn.hutool.json.JSONObject phoneInfo = phoneNumberObj.getJSONObject("phone_info");
        String phoneNumber = phoneInfo.getStr("phoneNumber");
        Lock lock = redissonClient.getLock(RedisKeyConstant.PHONE_CHANGE + phoneNumber);
        try
        {
            User user;
            lock.lock();
            UserExample userExample = new UserExample();
            userExample.createCriteria().andPhoneEqualTo(phoneNumber).andDisableEqualTo(false);
            if(userMapper.countByExample(userExample) > 0)
            {
                List<User> userList = userMapper.selectByExample(userExample);
                user = userList.get(0);
            }else
            {
                user = new User();
                user.setPhone(phoneNumber);
                user.setName(phoneNumber);
                user.setNickName(phoneNumber);
                user.setPassword(passwordEncoder.encode(phoneNumber));
                user.setUpdateTime(new Date());
                user.setBalance(new BigDecimal("0"));
                user.setDisable(false);
                user.setInvitationCode(AESUtils.encrypt(user.getPhone()));
                String invitationCode = Convert.toStr(queryUser.getInvitationCode());
                user.setCreateTime(new Date());
                userMapper.insertSelective(user);
                if(StringUtils.isNotEmpty(invitationCode))
                {
                    userExample = new UserExample();
                    userExample.createCriteria().andInvitationCodeEqualTo(invitationCode);
                    List<User> users = userMapper.selectByExample(userExample);
                    if(null != users && users.size() > 0)
                    {
                        User preatUser = users.get(0);
                        User updateUser = new User();
                        updateUser.setParentId(preatUser.getId());
                        updateUser.setId(user.getId());
                        userMapper.updateByPrimaryKeySelective(updateUser);
                        // 插入推广记录
                        PromotionInfo promotionInfo = new PromotionInfo();
                        promotionInfo.setUserId(updateUser.getId());
                        promotionInfo.setUpdateId(preatUser.getId());
                        promotionInfo.setCreateId(preatUser.getId());
                        promotionInfo.setCreateTime(new Date());
                        promotionInfo.setUpdateTime(new Date());
                        promotionInfoMapper.insertSelective(promotionInfo);
                        user = userMapper.selectByPrimaryKey(user.getId());
                    }
                }
            }
            return getAjaxResult(user);
        }catch (Exception exception)
        {
            exception.printStackTrace();
            return AjaxResult.error();
        }finally {
            lock.unlock();
        }
    }


    /*
     * @==============History===============<br/>
     * @Description //   用户绑定邀请码
     * @Date 2025-4-15 0:02
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult band(String code)
    {
        List<User> users = userMapper.selectByInvitationCode(code);

        if(null == users)
        {
            throw new BaseException("当前邀请码无效!");
        }
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();

        User preatUser = users.get(0);
        User updateUser = new User();
        updateUser.setParentId(preatUser.getId());
        updateUser.setId(loginUserDto.getId());

        // 插入推广记录
        PromotionInfo promotionInfo = new PromotionInfo();
        promotionInfo.setUserId(updateUser.getId());
        promotionInfo.setUpdateId(preatUser.getId());
        promotionInfo.setCreateId(preatUser.getId());
        promotionInfo.setCreateTime(new Date());
        promotionInfo.setUpdateTime(new Date());

        transactionTemplate.execute(transactionStatus -> {
            userMapper.updateByPrimaryKeySelective(updateUser);
            promotionInfoMapper.insertSelective(promotionInfo);
            return null;
        });
        return AjaxResult.success();
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户获取行程消息列表
     * @Date 2025-4-15 0:03
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult messageList()
    {
        MessageDto messageDto = new MessageDto();

        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        Long id = loginUserDto.getId();

        // 获取点赞已读总数
        // 获取点赞未读总数
        List<TripThumbs> tripThumbs = tripThumbsMapper.selectMessageList(id);
        int thumbsReadyTotal = 0;
        int thumbsNoReadyTotal = 0;

        List<TripThumbs> collect = tripThumbs.stream().filter(TripThumbs::getReady).collect(Collectors.toList());
        List<TripThumbs> collect2 = tripThumbs.stream().filter(item -> !item.getReady()).collect(Collectors.toList());
        if(collect.size() > 0)
        {
            thumbsReadyTotal = collect.size();
        }
        if(collect2.size() > 0)
        {
            thumbsNoReadyTotal = collect2.size();
        }
        messageDto.setThumbsReadyTotal(thumbsReadyTotal);
        messageDto.setThumbsNoReadyTotal(thumbsNoReadyTotal);
        messageDto.setThumbsReadyList(collect);
        messageDto.setThumbsNoReadyList(collect2);


        // 获取评论已读总数
        // 获取评论未读总数
        int commentReadyTotal = 0;
        int commentNoReadyTotal = 0;

        List<TripComment> tripComments = tripCommentMapper.selectMessageList(id);

        List<TripComment> collect3 = tripComments.stream().filter(TripComment::getReady).collect(Collectors.toList());
        List<TripComment> collect4 = tripComments.stream().filter(item -> !item.getReady()).collect(Collectors.toList());

        if(collect3.size() > 0)
        {
            commentReadyTotal = collect3.size();
        }
        if(collect4.size() > 0)
        {
            commentNoReadyTotal = collect4.size();
        }

        messageDto.setCommentReadyTotal(commentReadyTotal);
        messageDto.setCommentNoReadyTotal(commentNoReadyTotal);
        messageDto.setCommentReadyList(collect3);
        messageDto.setCommentNoReadyList(collect4);

        return AjaxResult.success(messageDto);
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户更新行程消息列表
     * @Date 2025-4-15 0:32
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult delMessageDto(DelMessageDto delMessageDto)
    {
        List<Long> commentReadyList = delMessageDto.getCommentReadyList();

        if(null != commentReadyList && commentReadyList.size() >0)
        {
            tripCommentMapper.delMessageList(commentReadyList);
        }
        List<Long> thumbsReadyList = delMessageDto.getThumbsReadyList();
        if(null != thumbsReadyList && thumbsReadyList.size() >0)
        {
            tripThumbsMapper.delMessageList(thumbsReadyList);
        }
        return AjaxResult.success();
    }

    @Override
    public AjaxResult collect(CollectDto collectDto)
    {
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        Collect collect = new Collect();
        collect.setCreateId(loginUserDto.getId());
        collect.setType(collectDto.getType());
        collect.setPrimaryId(collectDto.getPrimaryId());
        return collectMapper.insertSelective(collect) > 0 ? AjaxResult.success() : AjaxResult.error();
    }

    @Override
    public AjaxResult collectList()
    {
        CollectReturnDto collectReturnDto = new CollectReturnDto();
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        CollectExample collectExample = new CollectExample();
        collectExample.createCriteria().andCreateIdEqualTo(loginUserDto.getId());
        List<Collect> collects = collectMapper.selectByExample(collectExample);
        if(null != collects && collects.size() > 0)
        {
            Map<String, List<Long>> collect = collects.stream()
                    .collect(Collectors.groupingBy(
                            Collect::getType, // 根据type字段分组
                            Collectors.mapping(Collect::getPrimaryId, Collectors.toList()) // 提取primaryId并收集到列表
                    ));
            List<Long> cancel_list = collect.get(CollectEnum.CANCEL.getCode());// 文章列表id
            List<Long> payed_list = collect.get(CollectEnum.PAYED.getCode());// 行程列表id
            if(null != cancel_list && cancel_list.size() > 0)
            {
              List<ArticleInfo> articleInfos =  collectMapper.selectArticleList(cancel_list,loginUserDto.getId());
              collectReturnDto.setArticleList(articleInfos);
            }
            if(null != payed_list && payed_list.size() > 0)
            {
              List<TripInfo> tripInfoList =  collectMapper.selectPayedList(payed_list,loginUserDto.getId());
              collectReturnDto.setTripInfoList(tripInfoList);
            }

        }
        return AjaxResult.success(collectReturnDto);
    }

    @Override
    public AjaxResult cancelCollect(Long collectId)
    {
        return collectMapper.deleteByPrimaryKey(collectId) > 0 ? AjaxResult.success() : AjaxResult.error();
    }


    /*
     * @==============History===============<br/>
     * @Description //   微信手机登录鉴权
     * @Date 2025-4-15 0:00
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return java.lang.String
     */
    public String getAccessToken() throws Exception
    {
        String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", Constants.APPLET_APPID, Constants.APPLET_SECRET);
        URL obj = new URL(url);
        HttpURLConnection con = (HttpURLConnection) obj.openConnection();

        // 设置请求属性
        con.setRequestMethod("GET");
        con.setRequestProperty("Content-Type", "application/json");

        int responseCode = con.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK)
        {
            BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8));
            String inputLine;
            StringBuilder response = new StringBuilder();

            while ((inputLine = in.readLine()) != null)
            {
                response.append(inputLine);
            }
            in.close();

            JSONObject jsonObject = JSON.parseObject(response.toString());
            return jsonObject.getString("access_token");

        } else
        {
            throw new Exception("微信手机登录鉴权失败" + responseCode);
        }
    }


    /*
     * @==============History===============<br/>
     * @Description //   获取用户手机号
     * @Date 2025-4-15 0:00
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return cn.hutool.json.JSONObject
     */
    public cn.hutool.json.JSONObject getPhoneNumber(String code, String accessToken)
    {
        String result = null;
        try {
            // 接口调用凭证：accessToken
            String baseUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
            HashMap<String, Object> requestParam = new HashMap<>();
            // 手机号调用凭证
            requestParam.put("code", code);
            // 发送post请求读取调用微信接口获取openid用户唯一标识
            String jsonStr = JSONUtil.toJsonStr(requestParam);
            HttpResponse response = HttpRequest.post(baseUrl)
                    .header(Header.CONTENT_ENCODING, "UTF-8")
                    // 发送json数据需要设置contentType
                    .header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded")
                    .body(jsonStr)
                    .execute();
            if (response.getStatus() == HttpStatus.HTTP_OK)
            {
                result = response.body();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return JSONUtil.parseObj(result);
    }






    private String getTokenKey(String uuid)
    {
        return CacheConstants.LOGIN_APP_TOKEN_KEY + uuid;
    }


}
